//  Copyright (c) Microsoft Corporation.  All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.ServiceModel;

// Multi-party chat application using Peer Channel (a multi-party channel)
// If you are unfamiliar with new concepts used in this sample, refer to the WCF Basic\GettingStarted sample.

namespace DevChat.Chat
{
    public class ChatApp : IChat
    {
        private static string _Member;
        private static IChatChannel _Participant;

        public ChatApp(string member)
        {
            _Member = member;
            Members = new List<string>();
        }

        public static List<string> Members { get; set; }

        public void Chat(string member, string msg)
        {
            ConsoleWriteLine("[{0}] {1}", member, msg);
            if (member != _Member)
            {
                FlashWindow(Process.GetCurrentProcess().MainWindowHandle);
            }
        }

        // IChat implementation
        public void Join(string member)
        {
            Members.Add(member);
            ConsoleWriteLine("[{0} joined]", member);
        }

        public void Leave(string member)
        {
            Members.Remove(member);
            ConsoleWriteLine("[{0} left]", member);
        }

        // Host the chat instance within this EXE console application.
        public static void Main()
        {
            DefaultCustomResolver.Go();
            AppDomain.CurrentDomain.ProcessExit += (sender, e) => _Participant.Leave(_Member);

            ConsoleWriteLine("Enter your nickname:");
            string member = Console.ReadLine();

            if (member == string.Empty)
            {
                member = "noname" + new Random().Next(10);
            }

            // Construct InstanceContext to handle messages on callback interface. 
            // An instance of ChatApp is created and passed to the InstanceContext.
            InstanceContext instanceContext = new InstanceContext(new ChatApp(member));

            // Create the participant with the given endpoint configuration
            // Each participant opens a duplex channel to the mesh
            // participant is an instance of the chat application that has opened a channel to the mesh
            var factory = new DuplexChannelFactory<IChatChannel>(instanceContext, "ChatEndpoint");

            _Participant = factory.CreateChannel();

            // Retrieve the PeerNode associated with the participant and register for online/offline events
            // PeerNode represents a node in the mesh. Mesh is the named collection of connected nodes.
            IOnlineStatus ostat = _Participant.GetProperty<IOnlineStatus>();
            ostat.Online += (sender, e) => ConsoleWriteLine("**  Online");
            ostat.Offline += (sender, e) => ConsoleWriteLine("**  Offline");

            try
            {
                _Participant.Open();
            }
            catch (CommunicationException)
            {
                ConsoleWriteLine("Could not find resolver.  If you are using a custom resolver, please ensure");
                ConsoleWriteLine("that the service is running before executing this sample.  Refer to the readme");
                ConsoleWriteLine("for more details.");
                return;
            }

            ConsoleWriteLine("{0} is ready", member);
            ConsoleWriteLine("Type chat messages after going Online");
            ConsoleWriteLine("Press q<ENTER> to terminate this instance.");
            ConsoleWriteLine("Press s<ENTER> to show status.");

            // Announce self to other participants
            _Participant.Join(member);

            // loop until the user quits
            while (true)
            {
                string line = Console.ReadLine();
                Console.SetCursorPosition(0, Console.CursorTop - 1);
                if (line == "q")
                {
                    break;
                }
                else if (line == "s")
                {
                    Array.ForEach(Members.ToArray(), a => ConsoleWriteLine("User " + a + " connected."));
                    break;
                }
                _Participant.Chat(member, line);
            }
            // Leave the mesh
            _Participant.Leave(member);
            _Participant.Close();
            factory.Close();
        }

        private static void ConsoleWriteLine(string line, params string[] arg)
        {
            ConsoleWriteLine(string.Format(line, arg));
        }

        private static void ConsoleWriteLine(string line)
        {
            Console.WriteLine(DateTime.Now.ToString("HH:mm") + " " + line);
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool FlashWindowEx(ref FLASHWINFO pwfi);

        [StructLayout(LayoutKind.Sequential)]
        public struct FLASHWINFO
        {
            public UInt32 cbSize;
            public IntPtr hwnd;
            public UInt32 dwFlags;
            public UInt32 uCount;
            public Int32 dwTimeout;
        }

        public const UInt32 FLASHW_ALL = 3;

        private static void FlashWindow(IntPtr hWnd)
        {
            FLASHWINFO fInfo = new FLASHWINFO();

            fInfo.cbSize = Convert.ToUInt32(Marshal.SizeOf(fInfo));
            fInfo.hwnd = hWnd;
            fInfo.dwFlags = FLASHW_ALL;
            fInfo.uCount = 5;
            fInfo.dwTimeout = 0;

            FlashWindowEx(ref fInfo);
        }
    }
}
